package core

import (
	"bytes"
	"fmt"
	"github.com/sirupsen/logrus"
	"os"
	"path"
	"sync"
)

// MyLog 自定义日志格式器，实现 logrus.Formatter 接口
type MyLog struct{}

// 颜色常量（ANSI 转义码），用于终端显示不同级别的日志颜色
const (
	red    = 31 // 红色（错误/致命/崩溃级别）
	yellow = 33 // 黄色（警告级别）
	blue   = 36 // 蓝色（默认级别）
	gray   = 37 // 灰色（调试/追踪级别）
)

// Format 实现日志格式化逻辑，返回自定义格式的日志字节流
// entry: 日志条目（包含时间、级别、消息、调用者等信息）
func (MyLog) Format(entry *logrus.Entry) ([]byte, error) {
	// 根据日志级别选择颜色
	var levelColor int
	switch entry.Level {
	case logrus.DebugLevel, logrus.TraceLevel:
		levelColor = gray // 调试/追踪级别用灰色
	case logrus.WarnLevel:
		levelColor = yellow // 警告级别用黄色
	case logrus.ErrorLevel, logrus.FatalLevel, logrus.PanicLevel:
		levelColor = red // 错误/致命/崩溃级别用红色
	default:
		levelColor = blue // 其他级别用蓝色
	}

	// 初始化字节缓冲区（优先使用 entry 自带的缓冲区）
	var b *bytes.Buffer
	if entry.Buffer != nil {
		b = entry.Buffer
	} else {
		b = &bytes.Buffer{}
	}

	// 自定义时间格式（年-月-日 时:分:秒）
	timestamp := entry.Time.Format("2006-01-02 15:04:05")

	// 如果开启了调用者信息（通过 logrus.SetReportCaller(true) 启用）
	if entry.HasCaller() {
		// 提取调用者信息（文件路径:行号、函数名）
		funcVal := entry.Caller.Function                                                 // 调用函数名
		fileVal := fmt.Sprintf("%s:%d", path.Base(entry.Caller.File), entry.Caller.Line) // 文件基础名:行号

		// 构造完整日志格式：[时间] [颜色级别] 文件:行号 函数名 消息内容（颜色结尾符 \x1b[0m）
		_, _ = fmt.Fprintf(b, "[%s] \x1b[%dm[%s]\x1b[0m %s %s %s\n",
			timestamp, levelColor, entry.Level, fileVal, funcVal, entry.Message)
	}

	return b.Bytes(), nil
}

// MyHook 自定义日志钩子，实现 logrus.Hook 接口，用于日志文件写入和轮换
type MyHook struct {
	file     *os.File   // 当前普通日志文件句柄（info.log）
	errFile  *os.File   // 当前错误日志文件句柄（err.log）
	fileDate string     // 当前日志文件对应的日期（格式：2006-01-02）
	logPath  string     // 日志文件根目录（如 "logs"）
	mu       sync.Mutex // 互斥锁，保证并发写日志的线程安全
}

// Fire 日志钩子核心方法，处理每条日志的实际写入逻辑
// entry: 需要处理的日志条目
func (hook *MyHook) Fire(entry *logrus.Entry) error {
	hook.mu.Lock()         // 加锁保证并发安全
	defer hook.mu.Unlock() // 函数结束自动解锁

	// 获取当前日志时间对应的日期（用于判断是否需要轮换文件）
	timer := entry.Time.Format("2006-01-02")

	// 将日志条目格式化为字符串（使用 logrus 默认格式或自定义格式）
	line, err := entry.String()
	if err != nil {
		return fmt.Errorf("failed to format log entry: %v", err)
	}

	// 如果当前日期与日志日期不一致，触发日志轮换
	if hook.fileDate != timer {
		if err := hook.rotateFiles(timer); err != nil {
			return err
		}
	}

	// 写入普通日志文件（所有级别）
	if _, err := hook.file.Write([]byte(line)); err != nil {
		return fmt.Errorf("failed to write to log my_file: %v", err)
	}

	// 写入错误日志文件（仅 Error 及更高级别）
	if entry.Level <= logrus.ErrorLevel {
		if _, err := hook.errFile.Write([]byte(line)); err != nil {
			return fmt.Errorf("failed to write to error log my_file: %v", err)
		}
	}

	return nil
}

// rotateFiles 日志轮换方法，根据日期创建新的日志目录和文件
// timer: 新的日志日期（格式：2006-01-02）
func (hook *MyHook) rotateFiles(timer string) error {
	// 关闭旧文件句柄（避免资源泄漏）
	if hook.file != nil {
		if err := hook.file.Close(); err != nil {
			return fmt.Errorf("failed to close log my_file: %v", err)
		}
	}
	if hook.errFile != nil {
		if err := hook.errFile.Close(); err != nil {
			return fmt.Errorf("failed to close error log my_file: %v", err)
		}
	}

	// 创建按日期分类的日志目录（如 logs/2024-05-20）
	dirName := fmt.Sprintf("%s/%s", hook.logPath, timer)
	if err := os.MkdirAll(dirName, os.ModePerm); err != nil {
		return fmt.Errorf("failed to create log directory: %v", err)
	}

	// 定义新日志文件路径（info.log 存储所有级别，err.log 仅存储 Error 及以上）
	infoFilename := fmt.Sprintf("%s/info.log", dirName)
	errFilename := fmt.Sprintf("%s/err.log", dirName)

	// 打开/创建普通日志文件（只写、追加、自动创建，权限 0600）
	var err error
	hook.file, err = os.OpenFile(infoFilename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	if err != nil {
		return fmt.Errorf("failed to open log my_file: %v", err)
	}

	// 打开/创建错误日志文件（同上）
	hook.errFile, err = os.OpenFile(errFilename, os.O_WRONLY|os.O_APPEND|os.O_CREATE, 0600)
	if err != nil {
		return fmt.Errorf("failed to open error log my_file: %v", err)
	}

	// 更新当前日志日期（避免重复轮换）
	hook.fileDate = timer
	return nil
}

// Levels 指定需要触发 Fire 方法的日志级别（这里返回所有级别）
func (hook *MyHook) Levels() []logrus.Level {
	return logrus.AllLevels
}

// InitLogger 初始化 logrus 日志配置（入口函数）
func InitLogger() {
	logrus.SetLevel(logrus.InfoLevel) // 设置日志级别（仅 Info 及以上级别会被处理）
	logrus.SetFormatter(MyLog{})      // 设置自定义日志格式器
	logrus.SetReportCaller(true)      // 启用调用者信息（记录日志调用的文件、行号、函数名）
	logrus.AddHook(&MyHook{           // 添加自定义日志钩子（处理文件写入和轮换）
		logPath: "logs", // 日志文件根目录（程序运行目录下的 logs 文件夹）
	})
}
